#include "StdAfx.h"
#include <boost/variant.hpp>
#include <boost/scoped_array.hpp>
#include "../sgp/Image.h"
#include "STCIImageLoader.h"

namespace ja2{ namespace sgp
{
	namespace
	{
		struct Indexed
		{
			std::uint32_t NumberOfColors;
			std::uint16_t NumberOfSubImages;
			std::uint8_t RedDepth;
			std::uint8_t GreenDepth;
			std::uint8_t BlueDepth;
		};

		//! STCI header.
		struct STCIHeader
		{
			//! Original size.
			std::uint32_t OriginalSize;
			//! Stored size.
			/*!
				Equal to m_OriginalSize if data uncompressed.
			*/
			std::uint32_t StoredSize;
			//! Transparent color value.
			boost::uint32_t TransparentColor;
			//! Width of image.
			std::uint16_t Width;
			//! Height of image.
			std::uint16_t Height;
			//! BPP as stored in file.
			std::uint8_t BPP;
			//! ?AppDataSize?
			std::uint32_t AppDataSize;
		};
	}

	ImagePtr_t STCIImageLoader::load_image(std::string const &Name, vfs::FileStream_t &File, std::uint_least16_t Flags)
	{
		static unsigned short const HEADER_SIZE = 64;
		static unsigned short const ID_LEN = 4;
		static unsigned short const FLAG_INDEXED = 0x0008;
		static unsigned short const FLAG_RGB = 0x0004;
		static unsigned short const ETRLE_COMPRESSED = 0x0020;
		static char const ID_STIRNG[] = "STCI";

		// Buffer used for reading		
		char buffer[512];
		char *p_buffer_iterator = buffer;
		// Union buffer
		static const unsigned short UNION_SIZE = 20;
		char union_buffer[UNION_SIZE];
		// Read header
		if(!File.read(buffer, HEADER_SIZE))
		{
			std::string const error("Problem reading STCI header.");
			JA2_DEBUG_MESSAGE(TOPIC_HIMAGE, DebugManager::DL_3, error.c_str());
			throw RuntimeException(error);
		}
		// Is it STCI image
		if(memcmp(p_buffer_iterator, ID_STIRNG, ID_LEN) != 0)
		{
			std::string const error("This is not a STCI file!");
			JA2_DEBUG_MESSAGE(TOPIC_HIMAGE, DebugManager::DL_3, error.c_str());
			throw RuntimeException(error);
		}
		p_buffer_iterator += ID_LEN;
		STCIHeader stci_header;
		// Fill original size
		memcpy(&stci_header.OriginalSize, p_buffer_iterator, 4);
		p_buffer_iterator += 4;
		// Fill stored size
		memcpy(&stci_header.StoredSize, p_buffer_iterator, 4);
		p_buffer_iterator += 4;
		// Fill transparent color value
		memcpy(&stci_header.TransparentColor, p_buffer_iterator, 4);
		p_buffer_iterator += 4;
		// Set flags
		boost::uint32_t flags;
		memcpy(&flags, p_buffer_iterator, 4);
		p_buffer_iterator += 4;
		// Fill width and height
		memcpy(&stci_header.Height, p_buffer_iterator, 2);
		p_buffer_iterator += 2;
		memcpy(&stci_header.Width, p_buffer_iterator, 2);
		p_buffer_iterator += 2;
		// Read data for (variant)
		memcpy(union_buffer, p_buffer_iterator, UNION_SIZE);
		p_buffer_iterator += UNION_SIZE;
		// Read bpp
		stci_header.BPP = *(p_buffer_iterator++);
		// Read AppDataSize?
		memcpy(&stci_header.AppDataSize, p_buffer_iterator, 4);
		// Create image
		ImagePtr_t p_image(new Image(stci_header.Width, stci_header.Height, Flags, Name));
		// Read pixel data
		if(flags & FLAG_RGB)
		{
				// Allocate memory for data
				ImageData16bpp::DataType_t p_image_data_565( new char[stci_header.StoredSize]);
				// Read data
				if(!File.read(p_image_data_565.get(), stci_header.StoredSize))
					throw RuntimeException("STCI: Cannot read RGB data.");
				// Set data
				p_image->set_image_data(IImageDataPtr_t(new ImageData16bpp(std::move(p_image_data_565), stci_header.StoredSize)));
		}
		else if(flags & FLAG_INDEXED)
		{
			static const unsigned short PALETTE_ELEMENT_SIZE = 3;
			Indexed indexed;
			// Fill indexed variant
			p_buffer_iterator = union_buffer;
			// Get number of colors
			memcpy(&indexed.NumberOfColors, p_buffer_iterator, 4);
			p_buffer_iterator += sizeof(4);
			// Get number of subimages
			memcpy(&indexed.NumberOfSubImages, p_buffer_iterator, 2);
			p_buffer_iterator += sizeof(4);
			// Get red depth
			indexed.RedDepth = *(p_buffer_iterator++);
			// Get green depth
			indexed.GreenDepth = *(p_buffer_iterator++);
			// get blue depth
			indexed.BlueDepth = *(p_buffer_iterator++);
			// Palette must be always 255 of size
			JA2_ASSERT_MESAGE(indexed.NumberOfColors == 256, "Number of palletes mismatch");
			// Create palette
			PaletteVector_t palette;
			{
				// Allocate for palette
				size_t const palette_size = indexed.NumberOfColors * PALETTE_ELEMENT_SIZE;
				std::unique_ptr<char[]> p_pallete(new char[palette_size]);
				p_buffer_iterator = p_pallete.get();
				// Read palette from file
				if(!File.read(p_pallete.get(), palette_size) || File.gcount() != palette_size)
					throw RuntimeException("STCI: Cannot read palette.");
				palette.reserve(palette_size);
				for(size_t i = 0; i < indexed.NumberOfColors; ++i)
				{
					auto const palette_index = i * 3;
					palette.push_back(PaletteEntry(*(p_pallete.get() + palette_index + 2), *(p_pallete.get() + palette_index + 1), *(p_pallete.get() + palette_index)));
				}
			}
			// Create indexed image data
			std::unique_ptr<ImageDataIndexed> p_image_data_indexed(new ImageDataIndexed(indexed.NumberOfSubImages, std::move(palette)));
			// SubImage structure
			struct SubImage
			{
				std::uint32_t DataOffset;
				std::uint32_t DataLength;
				std::int16_t OffsetX;
				std::int16_t OffsetY;
				std::uint16_t Height;
				std::uint16_t Width;
			};
			// Load subimages
			static unsigned short const SUBIMAGE_SIZE = 16;
			size_t const sub_image_size = indexed.NumberOfSubImages * SUBIMAGE_SIZE;
			std::unique_ptr<char[]> p_ETRLE_buffer_new(new char[sub_image_size]);
			char *p_ETRLE_buffer = p_ETRLE_buffer_new.get();
			// Read sub image data
			if(!File.read(p_ETRLE_buffer, sub_image_size))
				throw RuntimeException("STCI: Cannot read ETRLE data.");
			// Read image data
			std::unique_ptr<char[]> p_image_data(new char[stci_header.StoredSize]);
			if(!File.read(p_image_data.get(), stci_header.StoredSize))
				throw std::runtime_error("STCI: Error reading image data.");
			// Load Application data
			if(stci_header.AppDataSize > 0)
				JA2_ASSERT_MESAGE(false, "Not implemented yet");
			Rect sub_image_rect;
			// Create pixel buffer for each subimage and add it to sprite description
			for(unsigned short i = 0; i < indexed.NumberOfSubImages; i++)
			{
				// Get subimage
				SubImage subimage;
				memcpy(&subimage.DataOffset, p_ETRLE_buffer, 4);
				p_ETRLE_buffer += 4;
				memcpy(&subimage.DataLength, p_ETRLE_buffer, 4);
				p_ETRLE_buffer += 4;
				memcpy(&subimage.OffsetX, p_ETRLE_buffer, 2);
				p_ETRLE_buffer += 2;
				memcpy(&subimage.OffsetY, p_ETRLE_buffer, 2);
				p_ETRLE_buffer += 2;
				memcpy(&subimage.Height, p_ETRLE_buffer, 2);
				p_ETRLE_buffer += 2;
				memcpy(&subimage.Width, p_ETRLE_buffer, 2);
				p_ETRLE_buffer += 2;
				// 8bpp buffer of new pixels
				std::unique_ptr<char[]> p_new_px_buffer(new char[subimage.DataLength]);
				std::memcpy(p_new_px_buffer.get(), p_image_data.get() + subimage.DataOffset, subimage.DataLength);
				// Need to uncompress data
				if((flags & ETRLE_COMPRESSED) == 0)
					JA2_ASSERT_MESAGE(false, "STCI: Unknown format.");
				// Add subimage with sub-image rect specified in x, y as sub-image index
				p_image_data_indexed->add_sub_image(std::move(p_new_px_buffer), Rect(i, 0, subimage.Width, subimage.Height));
			}
			p_image->set_image_data(std::move(p_image_data_indexed));
		}
	
		return p_image;
	}
} /*sgp*/} /*ja2*/